package socket;

import com.sun.org.apache.bcel.internal.generic.Select;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.security.Key;
import java.util.Set;

/**
 * 使用NIO完成聊天室服务器
 */
public class NIOServer {
    public void start(){
        //创建ServerSocketChannel
        try {
            //创建ServerSocketChannel
            ServerSocketChannel serverSocketChannel =ServerSocketChannel.open();
            //ServerSocketChannel默认是阻塞通道,可改为非阻塞通道.
            serverSocketChannel.configureBlocking(false);//设置为非阻塞
            //为ServerSocketChannel绑定端口,客户端通过该端口进行链接
            serverSocketChannel.bind(new InetSocketAddress(8089));

            /*
                创建多路选择器
                这个是NIO实现非阻塞的关键API,用于监控多路设备的时间,并可以做出响应.
                使得单个线程仅需要轮询多路选择器就可以对多个设备做出事件处理
             */
            Selector selector = Selector.open();
            /*
                将ServerSocketChannel注册代多路选择器上,让其监控是否有客户端连接的事件
             */
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            //让主线程一直关注选择器是否有事件需要处理了
            while(true){
                //当选择器发现注册在它上面的某些通道有事件时,该方法会立即返回,否则会堵塞
                selector.select();
                //开始处理事件


                //通过选择器获取目前所有有事件的通道
                Set<SelectionKey> keySet =selector.selectedKeys();

                //遍历并处理每一个事件
                for(SelectionKey key :keySet){
                    //判断该事件是否为可接受客户端连接的(ServerSocketChannel注册的OP_ACCEPT所响应的事件)
                    if (key.isAcceptable()){
                        //获取该事件对应通道
                        ServerSocketChannel scc =(ServerSocketChannel) key.channel();
                        //"接电话"
                        SocketChannel socket =scc.accept();
                        //非阻塞的ServerSocketChannel的accept方法可能返回null
                        if (serverSocketChannel==null){
                            continue;
                        }
                        //将SocketChannel设置为非阻塞式
                        socket.configureBlocking(false);
                        //将SocketChannel注册到多路选择器上,关心的事件为:有数据可读取(该客户端发消息过来)
                        serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
                        System.out.println("一个客户端链接了!");
                    }else if(key.isReadable()){
                        SocketChannel sc =(SocketChannel)key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        sc.read(buffer);//读取客户端发送过里啊的所有数据存入buffer
                        buffer.flip();//position:0 limit:前进读进来的所有字节
                        //获取缓冲区中本次读取到的所有字节:0--limit
                        //创建一个与buffer中目前可用字节一样长的一个字节数组
                        byte[]data=new byte[buffer.limit()];
                        //要求buffer将所有可用字节存入我们传入的字节数组data中
                        buffer.get(data);

                    }
                }
            }



        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        NIOServer server = new NIOServer();
        server.start();
    }
}
